/**
 * @class   wosh::WoshHost
 * @brief   The class holds basic information about a generic WOSH host over
 *          the distributed system.
 *
 * @see     wosh::NetworkManager
 * @see     wosh::services::DiscoveryBundle
 ****************************************************************************
 * @version $Id: WoshHost.h 2863 2010-08-07 04:28:58Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

#ifndef __WOSH_Core_WoshHost_H__
 #define __WOSH_Core_WoshHost_H__

 #include <woshDefs.h>
 #include <core/Object.h>
 #include <core/Variant.h>
 #include <core/Fact.h>
 #include <map>


namespace wosh {

 #define _WoshHost_KEY_Seen						"Seen"
 #define _WoshHost_KEY_SeenRemote				"SeenRemote"

 #define _WoshHost_KEY_DiscoverSource			"DiscoverSource"
 #define _WoshHost_KEY_DiscoveryBindAddress		"DiscoveryBindAddress"
 #define _WoshHost_KEY_DiscoveryProtocolVersion	"DiscoveryProtocolVersion"

 #define _WoshHost_KEY_NotifyFrequency			"NotifyFrequency"
 #define _WoshHost_KEY_KernelState				"KernelState"
 #define _WoshHost_KEY_KernelType				"KernelType"
 #define _WoshHost_KEY_ProtocolCount			"ProtocolCount"
 #define _WoshHost_KEY_ConnectionCount			"ConnectionCount"
 #define _WoshHost_KEY_Protocols				"Protocols"
 #define _WoshHost_KEY_Connections				"Connections"
 #define _WoshHost_KEY_Neighbours				"Neighbours"
 #define _WoshHost_KEY_HopsCount				"HopsCount"

 #define _WoshHost_EVENT_Updated				"WoshHost_Updated"
 #define _WoshHost_EVENT_ListChanged			"WoshHost_ListChanged"
 #define _WoshHost_EVENT_ConnectionChanged		"WoshHost_ConnectionChanged"

 #define _WoshHost_Connected					1
 #define _WoshHost_Disconnected					2


class Table;
class Property;
template <class T> class DataFieldCollector;

class WoshHost :	public wosh::Object,
					public virtual ISerializable
 {
	WOSH_SERIALIZABLE(wosh::WoshHost)
	WOSH_COMPARE_RTTI(wosh::WoshHost)

	public:
		/**
		 * @brief  Empty constructor.
		 */
		WoshHost();
		/**
		 * @brief  Constructor with initializer. 
		 * @param name [in] the name of the host (such as 'wosh23')
		 */
		WoshHost( const std::string& name );
		/**
		 * @brief  Copy constructor.
		 * @param m [in] the source object to be copied
		 */
		WoshHost( const WoshHost& m );
		/**
		 * @brief  Empty destructor.
		 */
		virtual ~WoshHost() { }


/** @name Getters
 * @{
 ******************************************************************************/
	public:
		/**
		 * @brief  Get the timestamp (epoch) of the object. Timestamp is related to local time.
		 *         Refers to last-seen timestamp of the remote host (by local-host point of view)
		 * @see    setSeen(), getSeenRemote()
		 */
		inline long getSeen() const										{ return this->lastSeen; }
		/**
		 * @brief  Get the timestamp (epoch) of the object. Timestamp is related to remote time.
		 *         Refers to last-seen timestamp of the remote host (by local-host point of view)
		 *         Generally, the difference with getSeen() refers to the time-offset between hosts.
		 * @see    setSeen(), getSeen()
		 */
		inline long getSeenRemote() const								{ return this->lastSeenRemote; }

		/**
		 * @brief  Get the source of the remote host's Discovery service (which sent the dicovery packet)
		 * @return a string representating address and port (such as 192.168.0.1:2722)
		 * @see    setDiscoverSource()
		 */
		inline const std::string& getDiscoverySource() const			{ return this->discoverSource; }

		/**
		 * @brief  Get the binding address of the remote host's Discovery service (which listen for discovery packets)
		 * @return a string representating address and port (such as 192.168.0.1:2722)
		 * @see    setDiscoveryBindAddress()
		 */
		inline const std::string& getDiscoveryBindAddress() const		{ return this->discoverBindAddress; }

		/**
		 * @brief  Get the distance between local host and remote host (as number of hops).
		 *         The WoshHost representing localhost, will have this field set to 0,
		 *         a direct neighbour is 1-hop and so on.
		 * @return number of hops
		 * @see    setHopsCount()
		 */
		inline int getHopsCount() const									{ return this->hopsCount; }

		/**
		 * @brief  Get the (claimed) notification frequency of the discovery service.
		 *         In other words, it refers to the number of seconds elapsed between each
		 *         discovery notification. This field is set by the installed Discovery service.
		 * @return number of seconds (period)
		 * @see    setNotifyFrequency()
		 */
		inline int getNotifyFrequency() const							{ return this->notifyFrequency; }

		/**
		 * @brief  Evaluate if the host timed-out respect last-seen time and notify frequency.
		 *         
		 * @param [in] span_seconds the accurancy of the evaluation in seconds
		 * @return false if notify-frequency is zero (or lower);      
		 * @see    getSeen(), getNotifyFrequency()
		 */
		bool isTimedOut( long span_seconds = 0 ) const;

		/**
		 * @brief  Get the version of the Discovery protocol. This field is set by Discovery service.
		 * @return a representation of the protocol version.
		 * @see    setProtocolVersion()
		 */
		inline int getDiscoveryProtocolVersion() const					{ return this->discoveryProtocolVersion; }

		/**
		 * @brief  Get the state of the Kernel. 
		 * @return kernel state (WoshKernel::KERNEL_STATE) as integer
		 * @see    WoshKernel::getKernelState(), setKernelState()
		 */
		inline int getKernelState() const								{ return this->kernel_state; }

		/**
		 * @brief  Get the list of supported protocols. Elements order is undefined.
		 *         Each entry defines a supported protocol, such as TCP://192.168.0.1:9595 
		 * @return vector of strings, each string represents a supported protocol
		 * @see    setProtocols(), getConnections()
		 */
		inline const std::vector<std::string>& getProtocols() const		{ return this->protocols; }
		inline std::vector<std::string>& getProtocols()					{ return this->protocols; }


		inline const std::map<std::string, long>& getConnections() const{ return this->connections; }
		inline std::map<std::string, long>& getConnections()			{ return this->connections; }

		int isConnected() const;

		/**
		 * @brief  Get the list of neighbours of the host, pairs contain kernel-name:timestamp.
		 *         Elements order is undefined. Timestamp refers to remote time.
		 * @return map of name:epoch pairs
		 * @see    setNeighbours(), getSeenRemote()
		 */
		inline const std::map<std::string, long>& getNeighbours() const	{ return this->neighbours; }
		inline std::map<std::string, long>& getNeighbours()				{ return this->neighbours; }

		/**
		 * @brief  Get the list of neighbours of the host. Elements order is undefined.
		 *         Provided for convenience. Basically extracts the key fields of getNeighbours() call.
		 * @return vector of strings, each string represents kernel name
		 * @see    getNeighbours(), setNeighbours()
		 */
		std::vector<std::string> getNeighboursName() const;

		/**
		 * @brief  Get the value of givedn field as Property.
		 *         Permission is inherited by current object permission.
		 * @param [in] key the key field of the property (see defines)
		 * @return new Property object, caller must care about releasing memory
		 * @see    getProperties()
		 */
		Property* getProperty( const std::string& key ) const;

		/**
		 * @brief  Get the properties of the object, collected in a table.
		 *         Properties are stored as Variant (two columns: key, value)
		 * @return new Table object, caller must care about releasing memory
		 */
		Table* getProperties() const;

		static DataFieldCollector<WoshHost>* getDataFields();

//@}
/** @name Setters
 * @{
 ******************************************************************************/
	public:
		using Object::setParentObject;

		/**
		 * @brief  Set the local timestamp of the object to current local time (epoch).
		 *         Refers to last-seen timestamp of the remote host (by local-host point of view)
		 * @see    setSeen(-1), getSeen()
		 */
		inline void setSeen()											{ setSeen(-1); }
		/**
		 * @brief  Set the local timestamp (epoch) of the object (respect to local time).
		 * @param time [in] timestamp to be set, as epoch (seconds after 1970), when
		 *         argument is set to '-1', function will set time using Utilities::std_time() call.
		 * @see    getSeen()
		 */
		void setSeen( long time );
		/**
		 * @brief  Set the remote timestamp (epoch) of the object.
		 *         Refers to last-seen timestamp of the remote host, but instead of local epoch (as setSeen())
		 *         it stores the remote time. On same host (or two hosts absolutely time-synchronized)
		 *         it will match the local timestamp, but usually this is not true/possible.
		 *         This field is used mostly by NetworkManager within statistical and message-ordering routines.
		 * @param time [in] timestamp to be set, as epoch (seconds after 1970)
		 * @see    getSeenRemote()
		 */
		inline void setSeenRemote( long time )							{ this->lastSeenRemote = time; }

		/**
		 * @brief  Set the source of the remote host's Discovery service (which sent the dicovery packet)
		 * @param value [in] a string representating address and port (such as 192.168.0.1:2722)
		 * @see    getDiscoverSource()
		 */
		inline  void setDiscoverSource( const std::string& value )		{ this->discoverSource = value; }

		/**
		 * @brief  Set the binding address of the remote host's Discovery service (which listen for discovery packets)
		 * @param value [in] a string representating address and port (such as 192.168.0.1:2722)
		 * @see    getDiscoveryBindAddress()
		 */
		inline void setDiscoveryBindAddress( const std::string& value )	{ this->discoverBindAddress = value; }

		/**
		 * @brief  Set the version of the Discovery protocol. This field is set by Discovery service.
		 * @param version [in] a representation of the protocol version.
		 * @see    getDiscoveryProtocolVersion()
		 */
		inline void setDiscoveryProtocolVersion( int version )			{ this->discoveryProtocolVersion = version; }

		/**
		 * @brief  Set the distance between local host and remote host (as number of hops).
		 *         The WoshHost representing localhost, will have this field set to 0,
		 *         a direct neighbour is 1-hop and so on.
		 * @param [in] distance_hops number of hops       
		 * @see    getHopsCount()
		 */
		inline void setHopsCount( int distance_hops )					{ this->hopsCount = distance_hops; }

		inline void setNotifyFrequency( int value )						{ this->notifyFrequency = value; }

		/**
		 * @brief  Set the state of the Kernel. 
		 * @param [in] value kernel state (WoshKernel::KERNEL_STATE) as integer
		 * @see    WoshKernel::getKernelState()
		 */
		inline void setKernelState( int value ) 						{ this->kernel_state = value; }

		void setProtocolSupported( const std::string& value );

		inline void setConnections( const std::map<std::string, long>& conns ) { this->connections = conns; }

		inline void setProtocols( const std::vector<std::string>& protos ) { this->protocols = protos; }

		/**
		 * @brief  Set the list of neighbours of the host, pairs contain kernel-name:timestamp.
		 *         Elements order is undefined. Timestamp refers to remote time.
		 * @param [in] neighb map of name:epoch pairs
		 * @see    getNeighbours()
		 */
		inline void setNeighbours( const std::map<std::string, long>& neighb ) { this->neighbours = neighb; }

		/**
		 * @brief  Clear the object (all fields).
		 */
		void clear();

//@}
/** @name Operators Overloading
 * @{
 ******************************************************************************/
	public:
		virtual WoshHost& operator=(const WoshHost& m);
		virtual bool operator==(const WoshHost &other) const;
		virtual bool operator!=(const WoshHost &other) const	{ return !(*this == other);  }
//@}
/** @name Serialization interface
 * @{
 ******************************************************************************/
	public:
		virtual std::string toString() const;
		virtual bool updateHash( DataHashContext* hash_context ) const;
//@}

	protected:
		std::string discoverSource;			///< IP address/port of the remote discovery service
		std::string discoverBindAddress;	///< IP address/port of the local discovery service

		int hopsCount;						///< numbers of hops needed to send a message from localhost
		int kernel_state;					///< state of the kernel - NOT USED YET
		int discoveryProtocolVersion;		///< version of the discovery protocol

		int notifyFrequency;				///< remote (push-)notification period in seconds
		long lastSeen;						///< when (epoch) host was last seen (by local-host)
		long lastSeenRemote;				///< when (epoch) host was last seen (remote time)

		std::vector<std::string> protocols;			///< supported protocols
		std::map<std::string, long> connections;	///< protocol => channel id
		std::map<std::string, long> neighbours;		///< neighbour name => epoch


}; // class def


/**
 * @class   wosh::WoshHost_ListChanged
 * @brief   Raised when 
 *
 * Internally, data is stored as wosh::Variant (STRING).
 *
 * Fields are:
 *  - \c 
 *

 * \code
 * \endcode
 *
 * @see     wosh::Fact
 * @see     wosh::WoshHost
 ****************************************************************************
 * @version 0.8.399 $Id: WoshHost.h 2863 2010-08-07 04:28:58Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
class WoshHost_ListChanged : public Fact_ListChanged
 {
	WOSH_SERIALIZABLE(wosh::WoshHost_ListChanged)

	public:
		/**
		 * @brief  Empty constructor.
		 */
		WoshHost_ListChanged()
			: Fact_ListChanged( _WoshHost_EVENT_ListChanged ) { }

		WoshHost_ListChanged( ISerializable* item, FACT_LIST_EVENT list_event )
			: Fact_ListChanged( item, list_event, _WoshHost_EVENT_ListChanged ) { }

		/**
		 * @brief  Constructor with initializers.
		 * @param hostname [in] added, updated or removed item 
		 * @param list_event [in] raised event within list/item
		 */
		explicit WoshHost_ListChanged( const std::string& hostname, FACT_LIST_EVENT list_event )
			: Fact_ListChanged(hostname, list_event, _WoshHost_EVENT_ListChanged) { }
		/**
		 * @brief  Copy constructor.
		 * @param m [in] the source object to be copied
		 */
		WoshHost_ListChanged( const WoshHost_ListChanged& m )
			: Fact_ListChanged(m) { }
		/**
		 * @brief  Empty destructor.
		 */
		virtual ~WoshHost_ListChanged() { }

	public:
		std::string getWoshHostName() const;

}; // class def



/**
 * @class   wosh::WoshHost_Updated
 * @brief   Raised when 
 *
 * Internally, data is stored as wosh::Variant (STRING).
 *
 * Fields are:
 *  - \c 
 *

 * \code
 * \endcode
 *
 * @see     wosh::Fact
 * @see     wosh::WoshHost
 ****************************************************************************
 * @version 0.8.399 $Id: WoshHost.h 2863 2010-08-07 04:28:58Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
class WoshHost_ConnectionChanged : public Fact
 {
	WOSH_SERIALIZABLE(wosh::WoshHost_ConnectionChanged)

	public:
		/**
		 * @brief  Empty constructor.
		 */
		WoshHost_ConnectionChanged()
			: Fact( _WoshHost_EVENT_ConnectionChanged ) { }
		/**
		 * @brief  Constructor with initializers.
		 * @param woshname [in] the subject (host) of the event
		 */
		WoshHost_ConnectionChanged( WoshHost* host, int connection_state );

		WoshHost_ConnectionChanged( const std::string& hostname, int connection_state );
		/**
		 * @brief  Copy constructor.
		 * @param m [in] the source object to be copied
		 */
		WoshHost_ConnectionChanged( const WoshHost_ConnectionChanged& m )
			: Fact(m) { }
		/**
		 * @brief  Empty destructor.
		 */
		virtual ~WoshHost_ConnectionChanged() { }

	public:
		const WoshHost* getWoshHost() const;
		int getConnectionState() const;

		inline bool isHostConnected() const			{ return ( getConnectionState() == _WoshHost_Connected ); }
		inline bool isHostDisconnected() const		{ return ( getConnectionState() == _WoshHost_Disconnected ); }

	public:
		std::string getWoshHostName() const;

	public:
		virtual std::string toString() const;

}; // class def

}; // namespace wosh

#endif //__WOSH_Core_WoshHost_H__
